package core;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.*;


public class Filter extends UnitAbstract implements ActionListener{
	
	private transient double sampleRate;
	private double bias1 = 0.0;
	
	/** i/o pointer for input signal ringbuffer */
	private transient int px;
	
	/** i/o pointer for output signal ringbuffer */ 
	private transient int py;
	
	/** input signal ringbuffer */
	private transient double [] xv;
	
	/** output signal ringbuffer */
	private transient double [] yv;
	
	/** filter coefficients A (applied to prevously filtered signal) */
	private transient double [] a;
	
	/** filter coefficients B (applied to input signal */
	private transient double [] b;
	
	private FilterCoeffGen coeffGen;
	
	private static List<FilterCoeffGen> coeffGenList = new ArrayList<FilterCoeffGen>();
	public static void registerFilterCoeffGen(FilterCoeffGen cg){
		coeffGenList.add(cg);
	}
	private static FilterCoeffGen getInstance(FilterCoeffGen cg){
		try {
			return cg.getClass().newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	
	
	
	public Filter(FilterCoeffGen cg) {
		inputs = new String[]{"I"};
		outputs = new String[]{"O"};
		coeffGen = cg;
	}
	public void setCoeffGen(FilterCoeffGen cg){
		coeffGen = cg;
	}
	
	public void read(DataPackage dataPack, String tPort) {

		if(coeffGen.isChanged() || sampleRate != dataPack.sampleRate){
			sampleRate = dataPack.sampleRate;
			updateCoeff();
		}
		
			
		for (int i = 0; i < dataPack.data.length; i++) {
		    // get the new sample
			dataPack.data[i]+=bias1;
			xv[px] = dataPack.data[i];
			
			// compute the output
			dataPack.data[i] = b[0] * xv[px];
			for (int j = 1; j < b.length; ++j)
				dataPack.data[i] += b[j] * xv[(px - j + xv.length) % xv.length];
			
			for (int j = 1; j < a.length; ++j)
				dataPack.data[i] -= a[j] * yv[(py - j + yv.length) % yv.length];
			
			// save the result
			yv[py] = dataPack.data[i];
			
			// increment the index of the ring buffer
	        px = (px + 1) % xv.length;
	        py = (py + 1) % yv.length;
		}

		sendData(dataPack);
    }	  

	private void updateCoeff() {
		
        this.b = coeffGen.getCoefficientsB(sampleRate);
        this.a = coeffGen.getCoefficientsA(sampleRate);
        
        xv = new double [b.length + 1];
        px = 0;
        yv = new double [a.length + 1];
        py = 0;
    }
	@Override
	public int getInputsCount() {
		return 1;
	}

	@Override
	public int getOutputsCount() {
		return 1;
	}
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
		in.defaultReadObject();
		//updateCoeff();
	}
	
	JComboBox comboBox;
	JPanel coeffPanel;
	JFrame frame;
	@Override
	public JFrame getFrame() {
		if(frame != null){
			return frame;
		}
		frame = new JFrame(toString());
		
		frame.setLayout(new BorderLayout());
		
		JPanel filterPanel = new JPanel();
		frame.add(BorderLayout.NORTH, filterPanel);
		
		coeffPanel = coeffGen.getPanel();
		frame.add(BorderLayout.CENTER, coeffPanel);
		
		filterPanel.setLayout(new GridLayout(1,2));
		
		filterPanel.add(new Label("Typ filtru"));
		comboBox = new JComboBox(coeffGenList.toArray());
		
		filterPanel.add(comboBox);

		comboBox.addActionListener(this);
		
		frame.pack();
		frame.setLocationRelativeTo(null);
		
		return frame; //TODO
	}
	
	@Override
	public String toString() {
		return "Filtr";
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		setCoeffGen(getInstance((FilterCoeffGen) comboBox.getSelectedItem()));
		frame.remove(coeffPanel);
		coeffPanel = coeffGen.getPanel();
		frame.add(BorderLayout.CENTER, coeffPanel);
		frame.pack();
		
	}
	
	public double getBias1() {
		return bias1;
	}
	public void setBias1(double bias1) {
		this.bias1 = bias1;
	}
}
